package com.ruoyi.learn.java.design.pattern.struct;

/**
 * 装饰器模式 (Decorator Pattern) 示例：为咖啡动态添加调料。
 */
public class DecoratorPatternDemo {

    //region ========== 1. 组件接口 (Component Interface) ==========

    /**
     * 组件接口：饮料
     * 定义了所有具体组件和装饰器都必须实现的方法。
     */
    public interface Beverage {
        String getDescription(); // 获取饮料的描述
        double getCost();        // 获取饮料的成本
    }

    //endregion

    //region ========== 2. 具体组件 (Concrete Component) ==========

    /**
     * 具体组件：基础咖啡
     * 最基础的饮料，没有添加任何调料。
     */
    public static class Coffee implements Beverage {
        @Override
        public String getDescription() {
            return "咖啡";
        }

        @Override
        public double getCost() {
            return 5.0; // 基础价格 5元
        }
    }

    //endregion

    //region ========== 3. 装饰器 (Decorator) ==========

    /**
     * 装饰器抽象类
     * 实现了 Beverage 接口，并持有一个 Beverage 引用。
     * 它提供了默认的实现，通常是将请求转发给被装饰的对象。
     */
    public static abstract class CondimentDecorator implements Beverage {
        protected Beverage beverage; // 持有被装饰的饮料对象

        public CondimentDecorator(Beverage beverage) {
            this.beverage = beverage;
        }

        // 默认转发描述和成本，具体装饰器可以重写
        @Override
        public String getDescription() {
            return beverage.getDescription();
        }

        @Override
        public double getCost() {
            return beverage.getCost();
        }
    }

    //endregion

    //region ========== 4. 具体装饰器 (Concrete Decorators) ==========

    /**
     * 具体装饰器：牛奶
     * 为饮料添加牛奶。
     */
    public static class Milk extends CondimentDecorator {
        public Milk(Beverage beverage) {
            super(beverage);
        }

        @Override
        public String getDescription() {
            return beverage.getDescription() + ", 牛奶"; // 扩展描述
        }

        @Override
        public double getCost() {
            return beverage.getCost() + 1.5; // 增加成本
        }
    }

    /**
     * 具体装饰器：糖
     * 为饮料添加糖。
     */
    public static class Sugar extends CondimentDecorator {
        public Sugar(Beverage beverage) {
            super(beverage);
        }

        @Override
        public String getDescription() {
            return beverage.getDescription() + ", 糖";
        }

        @Override
        public double getCost() {
            return beverage.getCost() + 0.5;
        }
    }

    /**
     * 具体装饰器：巧克力
     * 为饮料添加巧克力。
     */
    public static class Chocolate extends CondimentDecorator {
        public Chocolate(Beverage beverage) {
            super(beverage);
        }

        @Override
        public String getDescription() {
            return beverage.getDescription() + ", 巧克力";
        }

        @Override
        public double getCost() {
            return beverage.getCost() + 2.0;
        }
    }

    //endregion

    //region ========== 5. 客户端 (Client) ==========

    /**
     * 订单系统客户端
     * 它只知道 Beverage 接口，不关心具体是哪种饮料或添加了什么调料。
     */
    public static class OrderClient {
        public void printOrder(Beverage beverage) {
            System.out.println("饮品: " + beverage.getDescription());
            System.out.printf("价格: ¥%.1f%n", beverage.getCost());
            System.out.println("---");
        }
    }

    //endregion

    //region ========== 6. 主函数 (Main) - 演示装饰器模式 ==========

    public static void main(String[] args) {
        System.out.println("=== 装饰器模式演示 ===\n");
        OrderClient client = new OrderClient();

        // 1. 创建一个基础咖啡
        Beverage coffee = new Coffee();
        System.out.println("1. 基础咖啡:");
        client.printOrder(coffee);

        // 2. 在基础咖啡上添加牛奶
        Beverage coffeeWithMilk = new Milk(coffee);
        System.out.println("2. 咖啡 + 牛奶:");
        client.printOrder(coffeeWithMilk);

        // 3. 在 (咖啡 + 牛奶) 上再添加糖
        Beverage coffeeWithMilkAndSugar = new Sugar(coffeeWithMilk);
        System.out.println("3. 咖啡 + 牛奶 + 糖:");
        client.printOrder(coffeeWithMilkAndSugar);

        // 4. 创建一个更复杂的订单：基础咖啡 + 巧克力 + 牛奶 + 糖
        // 注意：装饰的顺序会影响最终的描述，但不影响最终价格（加法交换律）
        Beverage fancyCoffee = new Coffee();
        fancyCoffee = new Chocolate(fancyCoffee); // 咖啡 + 巧克力
        fancyCoffee = new Milk(fancyCoffee);      // + 牛奶
        fancyCoffee = new Sugar(fancyCoffee);     // + 糖

        System.out.println("4. 豪华版咖啡 (巧克力 + 牛奶 + 糖):");
        client.printOrder(fancyCoffee);

        // 5. 演示独立性：另一个基础咖啡
        Beverage anotherCoffee = new Coffee();
        System.out.println("5. 另一个基础咖啡 (未被装饰):");
        client.printOrder(anotherCoffee); // 仍然是基础咖啡，不受其他装饰影响

        // 6. 错误示例：尝试修改被装饰的对象（不推荐，但演示独立性）
        // Coffee coffeeObj = (Coffee) coffee; // coffee 仍然是基础 Coffee
        // coffeeObj.setDescription("修改了!"); // Coffee 类没有此方法，无法修改
        // 实际上，基础 coffee 对象的状态没有被改变，装饰是通过组合包装实现的。
    }

    //endregion
}
